(function () {
    var test = require( 'tape' );
    var typeofit = require( '../typeofit' );

    const jsdom = require( 'jsdom' );
    const { JSDOM } = jsdom;
    const DOMParser = require( 'xmldom' ).DOMParser;
    const HTMLDOM = new JSDOM( '<html><head><title>isXML</title></head><body id="body"><p id="p1">text1</p><p id="p2">text2</p><p id="p3"> </p></body></html>' );

    var unset;
    var fn = function () {
    };
    var symbol = Symbol( 's' );
    var numbers = [
        1,
        2,
        3,
        4
    ];
    var robert = {
        'name': 'Robert'
    };
    var set = new Set( numbers );
    var weakset = new WeakSet( [
        robert,
        fn
    ] );
    var map = new Map( [
        [
            'name',
            'Robert'
        ],
        [
            'Gender',
            'Male'
        ]
    ] );
    var weakmap = new WeakMap();
    var regexp = /^\s*/ig;

    // typeofit.is()
    test( 'is', function ( t ) {
        var TYPE_BOOLEAN = 'boolean',
            TYPE_NULL = 'null',
            TYPE_UNDEFINED = 'undefined',
            TYPE_NUMBER = 'number',
            TYPE_STRING = 'string',
            TYPE_OBJECT = 'object',
            TYPE_ARRAY = 'array',
            TYPE_DATE = 'date',
            TYPE_ERROR = 'error',
            TYPE_COLLECTION = 'collection',
            TYPE_ARGUMENTS = 'arguments',
            TYPE_FUNCTION = 'function',
            TYPE_REGEXP = 'regexp',
            TYPE_SYMBOL = 'symbol',
            TYPE_SET = 'set',
            TYPE_WEAKSET = 'weakset',
            TYPE_MAP = 'map',
            TYPE_WEAKMAP = 'weakmap',
            TYPE_WHITESPACE = 'whitespace',
            TYPE_TEXT = 'text',
            TYPE_INT8_ARRAY = 'int8array',
            TYPE_UNIT8_ARRAY = 'uint8array',
            TYPE_UNIT8_CLAMPED_ARRAY = 'uint8clampedarray',
            TYPE_INT16_ARRAY = 'int16array',
            TYPE_UNIT16_ARRYA = 'uint16array',
            TYPE_INT32_ARRAY = 'int32array',
            TYPE_UNIT32_ARRAY = 'uint32array',
            TYPE_FLOAT32_ARRAY = 'float32array',
            TYPE_FLOAT64_ARRAY = 'float64array';

        /* ===== Primitive data types ===== */
        // Boolean
        t.equal( typeofit.is( true ), TYPE_BOOLEAN );
        // Null
        t.equal( typeofit.is( null ), TYPE_NULL );
        // Number
        t.equal( typeofit.is( 1 ), TYPE_NUMBER );
        // String
        t.equal( typeofit.is( 'robert' ), TYPE_STRING );
        // Symbol
        t.equal( typeofit.is( symbol ), TYPE_SYMBOL );
        // Undefined
        t.equal( typeofit.is( unset ), TYPE_UNDEFINED );

        /* ===== Array ===== */
        // Array
        t.equal( typeofit.is( numbers ), TYPE_ARRAY );

        // ArrayLike
        t.equal( typeofit.is( arguments ), TYPE_ARGUMENTS );

        // ES2017 Array
        t.equal( typeofit.is( new Int8Array() ), TYPE_INT8_ARRAY );
        t.equal( typeofit.is( new Uint8Array() ), TYPE_UNIT8_ARRAY );
        t.equal( typeofit.is( new Uint8ClampedArray() ), TYPE_UNIT8_CLAMPED_ARRAY );
        t.equal( typeofit.is( new Int16Array() ), TYPE_INT16_ARRAY );
        t.equal( typeofit.is( new Uint16Array() ), TYPE_UNIT16_ARRYA );
        t.equal( typeofit.is( new Int32Array() ), TYPE_INT32_ARRAY );
        t.equal( typeofit.is( new Uint32Array() ), TYPE_UNIT32_ARRAY );
        t.equal( typeofit.is( new Float32Array() ), TYPE_FLOAT32_ARRAY );
        t.equal( typeofit.is( new Float64Array() ), TYPE_FLOAT64_ARRAY );

        // Date
        t.equal( typeofit.is( new Date() ), TYPE_DATE );

        // Error
        t.equal( typeofit.is( new Error( 'THIS IS NEW ERROR' ) ), TYPE_ERROR );

        // Function
        t.equal( typeofit.is( fn ), TYPE_FUNCTION );

        // HTMLCollection
        t.equal( typeofit.is( HTMLDOM.window.document.querySelectorAll( 'p' ) ), TYPE_COLLECTION );

        // Map
        t.equal( typeofit.is( map ), TYPE_MAP );
        t.equal( typeofit.is( weakmap ), TYPE_WEAKMAP );

        // Object
        t.equal( typeofit.is( robert ), TYPE_OBJECT );

        // RegExp
        t.equal( typeofit.is( regexp ), TYPE_REGEXP );

        // Set
        t.equal( typeofit.is( set ), TYPE_SET );
        t.equal( typeofit.is( weakset ), TYPE_WEAKSET );

        // Text
        t.equal( typeofit.is( HTMLDOM.window.document.querySelector( '#p1' ).firstChild ), TYPE_TEXT );

        // White Space
        t.equal( typeofit.is( HTMLDOM.window.document.querySelector( '#p3' ).firstChild ), TYPE_WHITESPACE );

        t.end();
    } );

    // typeofit.isArray()
    test( 'isArray', function ( t ) {
        t.equal( typeofit.isArray( numbers ), true, true );
        t.equal( typeofit.isArray( arguments ), false, false );
        t.equal( typeofit.isArray( new Int8Array() ), false, false );

        t.end();
    } );

    // typeofit.isArrayLike()
    test( 'isArrayLike', function ( t ) {
        t.equal( typeofit.isArrayLike( numbers ), true, true );
        t.equal( typeofit.isArrayLike( arguments ), true, true );
        t.equal( typeofit.isArrayLike( new Int8Array() ), false, false );

        t.end();
    } );

    // typeofit.isArguments()
    test( 'isArrayBuffer', function ( t ) {
        t.equal( typeofit.isArguments( new ArrayBuffer( 8 ) ), false, false );
        t.equal( typeofit.isArguments( arguments ), true, true );

        t.end();
    } );

    // typeofit.isArrayBuffer()
    test( 'isArrayBuffer', function ( t ) {
        t.equal( typeofit.isArrayBuffer( new ArrayBuffer( 8 ) ), true, true );
        t.equal( typeofit.isArrayBuffer( arguments ), false, false );

        t.end();
    } );

    // typeofit.isInt8Array()
    test( 'isInt8Array', function ( t ) {
        t.equal( typeofit.isInt8Array( numbers ), false, false );
        t.equal( typeofit.isInt8Array( arguments ), false, false );
        t.equal( typeofit.isInt8Array( new Int8Array() ), true, true );

        t.end();
    } );

    // typeofit.isUint8Array()
    test( 'isUint8Array', function ( t ) {
        t.equal( typeofit.isUint8Array( numbers ), false, false );
        t.equal( typeofit.isUint8Array( arguments ), false, false );
        t.equal( typeofit.isUint8Array( new Uint8Array() ), true, true );

        t.end();
    } );

    // typeofit.isUint8ClampedArray()
    test( 'isUint8ClampedArray', function ( t ) {
        t.equal( typeofit.isUint8ClampedArray( numbers ), false, false );
        t.equal( typeofit.isUint8ClampedArray( arguments ), false, false );
        t.equal( typeofit.isUint8ClampedArray( new Uint8ClampedArray() ), true, true );

        t.end();
    } );

    // typeofit.isInt16Array()
    test( 'isInt16Array', function ( t ) {
        t.equal( typeofit.isInt16Array( numbers ), false, false );
        t.equal( typeofit.isInt16Array( arguments ), false, false );
        t.equal( typeofit.isInt16Array( new Int16Array() ), true, true );

        t.end();
    } );

    // typeofit.isUint16Array()
    test( 'isUint16Array', function ( t ) {
        t.equal( typeofit.isUint16Array( numbers ), false, false );
        t.equal( typeofit.isUint16Array( arguments ), false, false );
        t.equal( typeofit.isUint16Array( new Uint16Array() ), true, true );

        t.end();
    } );

    // typeofit.isInt32Array()
    test( 'isInt32Array', function ( t ) {
        t.equal( typeofit.isInt32Array( numbers ), false, false );
        t.equal( typeofit.isInt32Array( arguments ), false, false );
        t.equal( typeofit.isInt32Array( new Int32Array() ), true, true );

        t.end();
    } );

    // typeofit.isUint32Array()
    test( 'isUint32Array', function ( t ) {
        t.equal( typeofit.isUint32Array( numbers ), false, false );
        t.equal( typeofit.isUint32Array( arguments ), false, false );
        t.equal( typeofit.isUint32Array( new Uint32Array() ), true, true );

        t.end();
    } );

    // typeofit.isFloat32Array()
    test( 'isFloat32Array', function ( t ) {
        t.equal( typeofit.isFloat32Array( numbers ), false, false );
        t.equal( typeofit.isFloat32Array( arguments ), false, false );
        t.equal( typeofit.isFloat32Array( new Float32Array() ), true, true );

        t.end();
    } );

    // typeofit.isFloat64Array()
    test( 'isFloat64Array', function ( t ) {
        t.equal( typeofit.isFloat64Array( numbers ), false, false );
        t.equal( typeofit.isFloat64Array( arguments ), false, false );
        t.equal( typeofit.isFloat64Array( new Float64Array() ), true, true );

        t.end();
    } );

    test( 'isBase64', function ( t ) {
        t.equal( typeofit.isBase64( 'a2V5MT12YWx1ZTEma2V5Mj12YWx1ZTI=' ), true, true );
        t.equal( typeofit.isBase64( 'this is not a base64 code string.' ), false, false );

        t.end();
    } );

    test( 'isBlank', function ( t ) {
        t.equal( typeofit.isBlank( '  ' ), true, true );
        t.equal( typeofit.isBlank( '' ), true, true );
        t.equal( typeofit.isBlank( new String() ), false, false );

        t.end();
    } );

    test( 'isBoolean', function ( t ) {
        t.equal( typeofit.isBoolean( !!0 ), true, true );
        t.equal( typeofit.isBoolean( false ), true, true );
        t.equal( typeofit.isBoolean( true ), true, true );
        t.equal( typeofit.isBoolean( Boolean( '1' ) ), true, true );
        t.equal( typeofit.isBoolean( 1 ), false, false );
        t.equal( typeofit.isBoolean( new Boolean( '' ) ), false, false );
        t.equal( typeofit.isBoolean( '' ), false, false );
        t.equal( typeofit.isBoolean( null ), false, false );
        t.equal( typeofit.isBoolean( NaN ), false, false );
        t.equal( typeofit.isBoolean( unset ), false, false );

        t.end();
    } );

    test( 'isConstructor', function ( t ) {
        var fn = function () {
        };
        var ff = class {
        };
        var f = () => console.log( 'no constructable' );

        t.equal( typeofit.isConstructor( fn ), true, true );
        t.equal( typeofit.isConstructor( ff ), true, true );
        t.equal( typeofit.isConstructor( f ), false, false );
        t.equal( typeofit.isConstructor( Math ), false, false );
        t.equal( typeofit.isConstructor( Boolean ), true, true );
        t.equal( typeofit.isConstructor( Array ), true, true );
        t.equal( typeofit.isConstructor( Function ), true, true );
        t.equal( typeofit.isConstructor( Date ), true, true );
        t.equal( typeofit.isConstructor( RegExp ), true, true );
        t.equal( typeofit.isConstructor( Object ), true, true );

        t.end();
    } );

    // typeofit.isDataView()
    test( 'isDataView', function ( t ) {
        var buffer = new ArrayBuffer( 8 );
        var dv = new DataView( buffer );

        t.equal( typeofit.isDataView( dv ), true, true );
        t.equal( typeofit.isDataView( arguments ), false, false );

        t.end();
    } );

    test( 'isDate', function ( t ) {
        var time = new Date();

        t.equal( typeofit.isDate( time ), true, true );
        t.equal( typeofit.isDate( '2017-07-06' ), false, false );
        t.equal( typeofit.isDate( time.getFullYear() ), false, false );

        t.end();
    } );

    test( 'isElement', function ( t ) {
        var body = HTMLDOM.window.document.querySelector( '#body' ),
            text = body.querySelector( '#p1' ).firstChild;

        t.equal( typeofit.isElement( body ), true, true );
        t.equal( typeofit.isElement( text ), false, false );

        t.end();
    } );

    test( 'isEmpty', function ( t ) {
        t.equal( typeofit.isEmpty( '' ), true, true );
        t.equal( typeofit.isEmpty( String() ), true, true );
        t.equal( typeofit.isEmpty( new String() ), false, false );
        t.equal( typeofit.isEmpty( ' ' ), false, false );

        t.end();
    } );

    test( 'isEmptyObject', function ( t ) {
        function Person ( name, age ) {
            this.name = name;
            this.age = age;
        }

        t.equal( typeofit.isEmptyObject( {} ), true, true );
        t.equal( typeofit.isEmptyObject( [] ), true, true );
        t.equal( typeofit.isEmptyObject( Person ), true, true );
        t.equal( typeofit.isEmptyObject( new Array() ), true, true );
        t.equal( typeofit.isEmptyObject( new Boolean() ), true, true );
        t.equal( typeofit.isEmptyObject( new Function( 'alert(\'function\')' ) ), true, true );
        t.equal( typeofit.isEmptyObject( new Date( '2017-12-11' ) ), true, true );
        t.equal( typeofit.isEmptyObject( new Object() ), true, true );
        t.equal( typeofit.isEmptyObject( new RegExp( '\s+', 'ig' ) ), true, true );
        t.equal( typeofit.isEmptyObject( null ), true, true );
        t.equal( typeofit.isEmptyObject( new String() ), true, true );
        t.equal( typeofit.isEmptyObject( new String( 'string' ) ), false, false );
        t.equal( typeofit.isEmptyObject( new Person( 'robert', 30 ) ), false, false );
        t.equal( typeofit.isEmptyObject( { name: 'robert' } ), false, false );
        t.equal( typeofit.isEmptyObject( [ '' ] ), false, false );

        t.end();
    } );

    test( 'isError', function ( t ) {
        t.equal( typeofit.isError( new Error() ), true, true );
        t.equal( typeofit.isError( {} ), false, false );

        t.end();
    } );

    test( 'isEven', function ( t ) {
        t.equal( typeofit.isEven( 2 ), true, true );
        t.equal( typeofit.isEven( 3 ), false, false );
        t.equal( typeofit.isEven( 2.2 ), false, false );

        t.end();
    } );

    test( 'isFloat', function ( t ) {
        t.equal( typeofit.isFloat( 2.4 ), true, true );
        t.equal( typeofit.isFloat( Number( '3.4556645445E7' ) ), true, true );
        t.equal( typeofit.isFloat( Number( '3.4556645445E10' ) ), false, false );
        t.equal( typeofit.isFloat( 2 ), false, false );
        t.equal( typeofit.isFloat( 0xffffff ), false, false );
        t.equal( typeofit.isFloat( NaN ), false, false );

        t.end();
    } );

    test( 'isFunction', function ( t ) {
        t.equal( typeofit.isFunction( function empty () {
        } ), true, true );
        t.equal( typeofit.isFunction( parseInt ), true, true );
        t.equal( typeofit.isFunction( Array ), true, true );
        t.equal( typeofit.isFunction( Boolean ), true, true );
        t.equal( typeofit.isFunction( Date ), true, true );
        t.equal( typeofit.isFunction( Number ), true, true );
        t.equal( typeofit.isFunction( Object ), true, true );
        t.equal( typeofit.isFunction( RegExp ), true, true );
        t.equal( typeofit.isFunction( String ), true, true );
        t.equal( typeofit.isFunction( Math ), false, false );

        t.end();
    } );

    test( 'isNativeFunction', function ( t ) {
        var fn = function () {
        };
        var ff = class {
        };
        var f = () => console.log( 'no constructable' );

        t.equal( typeofit.isNativeFunction( fn ), false, false );
        t.equal( typeofit.isNativeFunction( ff ), false, false );
        t.equal( typeofit.isNativeFunction( f ), false, false );
        t.equal( typeofit.isNativeFunction( Math ), false, false );
        t.equal( typeofit.isNativeFunction( Boolean ), true, true );
        t.equal( typeofit.isNativeFunction( Array ), true, true );
        t.equal( typeofit.isNativeFunction( Function ), true, true );
        t.equal( typeofit.isNativeFunction( Date ), true, true );
        t.equal( typeofit.isNativeFunction( RegExp ), true, true );
        t.equal( typeofit.isNativeFunction( Object ), true, true );

        t.end();
    } );

    test( 'isHex', function ( t ) {
        t.equal( typeofit.isHex( '#ffffff' ), true, true );
        t.equal( typeofit.isHex( 'f3f3f3' ), true, true );
        t.equal( typeofit.isHex( '#h1f1f1' ), false, false );
        t.equal( typeofit.isHex( 'sdadfa' ), false, false );

        t.end();
    } );

    test( 'isHTMLCollection', function ( t ) {
        var doc = HTMLDOM.window.document,
            paragraphs = doc.querySelectorAll( 'p' ),
            text = doc.querySelector( '#p1' ).firstChild;

        t.equal( typeofit.isHTMLCollection( paragraphs ), true, true );
        t.equal( typeofit.isHTMLCollection( text ), false, false );

        t.end();
    } );

    test( 'isInteger', function ( t ) {
        t.equal( typeofit.isInfinite( 2.4 ), false, false );
        t.equal( typeofit.isInfinite( Infinity ), true, true );

        t.end();
    } );

    test( 'isInteger', function ( t ) {
        t.equal( typeofit.isInteger( 2.4 ), false, false );
        t.equal( typeofit.isInteger( 2 ), true, true );
        t.equal( typeofit.isInteger( 0xffffff ), true, true );
        t.equal( typeofit.isInteger( NaN ), false, false );
        t.equal( typeofit.isInteger( Number( '3.4556645445E7' ) ), false, false );

        t.end();
    } );

    test( 'isJSON', function ( t ) {
        t.equal( typeofit.isJSON( '"this is a JSON string."' ), true, true );
        t.equal( typeofit.isJSON( '["1","2",3]' ), true, true );
        t.equal( typeofit.isJSON( '3' ), true, true );
        t.equal( typeofit.isJSON( '{"prop":"JSON"}' ), true, true );
        t.equal( typeofit.isJSON( { prop: 'JSON' } ), false, false );
        t.equal( typeofit.isJSON( '{prop:\'JSON\'}' ), false, false );

        t.end();
    } );

    test( 'isMap', function ( t ) {
        t.equal( typeofit.isMap( map ), true, true );
        t.equal( typeofit.isMap( [
            'name',
            'robert'
        ] ), false, false );

        t.end();
    } );

    test( 'isNull', function ( t ) {
        t.equal( typeofit.isNull( null ), true, true );
        t.equal( typeofit.isNull( {} ), false, false );
        t.equal( typeofit.isNull( unset ), false, false );

        t.end();
    } );

    test( 'isNumber', function ( t ) {
        t.equal( typeofit.isNumber( 2 ), true, true );
        t.equal( typeofit.isNumber( 2.4 ), true, true );
        t.equal( typeofit.isNumber( 0xffffff ), true, true );
        t.equal( typeofit.isNumber( NaN ), true, true );
        t.equal( typeofit.isNumber( Number( '3.4556645445E7' ) ), true, true );
        t.equal( typeofit.isNumber( '33' ), false, false );

        t.end();
    } );

    test( 'isNumeric', function ( t ) {
        t.equal( typeofit.isNumeric( 2 ), true, true );
        t.equal( typeofit.isNumeric( 2.4 ), true, true );
        t.equal( typeofit.isNumeric( 0xffffff ), true, true );
        t.equal( typeofit.isNumeric( '33' ), true, true );
        t.equal( typeofit.isNumeric( '0xffffff' ), true, true );
        t.equal( typeofit.isNumeric( NaN ), false, false );

        t.end();
    } );

    test( 'isObject', function ( t ) {
        t.equal( typeofit.isObject( fn ), true, true );
        t.equal( typeofit.isObject( {} ), true, true );
        t.equal( typeofit.isObject( Object.create( null ) ), true, true );
        t.equal( typeofit.isObject( Object.create( {} ) ), true, true );
        t.equal( typeofit.isObject( new Function() ), true, true );
        t.equal( typeofit.isObject( [] ), false, false );
        t.equal( typeofit.isObject( null ), false, false );

        t.end();
    } );

    test( 'isOdd', function ( t ) {
        t.equal( typeofit.isOdd( 3 ), true, true );
        t.equal( typeofit.isOdd( 2 ), false, false );
        t.equal( typeofit.isOdd( 3.1 ), false, false );

        t.end();
    } );

    test( 'isPlainObject', function ( t ) {
        t.equal( typeofit.isPlainObject( {} ), true, true );
        t.equal( typeofit.isPlainObject( Object.create( null ) ), true, true );
        t.equal( typeofit.isPlainObject( fn ), false, false );
        t.equal( typeofit.isPlainObject( [] ), false, false );
        t.equal( typeofit.isPlainObject( null ), false, false );

        t.end();
    } );

    test( 'isRegExp', function ( t ) {
        t.equal( typeofit.isRegExp( new RegExp( '\s+', 'ig' ) ), true, true );
        t.equal( typeofit.isRegExp( /\s+/ig ), true, true );
        t.equal( typeofit.isRegExp( set ), false, false );

        t.end();
    } );

    test( 'isSet', function ( t ) {
        t.equal( typeofit.isSet( set ), true, true );
        t.equal( typeofit.isSet( [
            1,
            2,
            3,
            4
        ] ), false, false );

        t.end();
    } );

    test( 'isString', function ( t ) {
        t.equal( typeofit.isString( 'string' ), true, true );
        t.equal( typeofit.isString( String( 'string' ) ), true, true );
        t.equal( typeofit.isString( new String( 'string' ) ), false, false );

        t.end();
    } );

    test( 'isSymbol', function ( t ) {
        t.equal( typeofit.isSymbol( symbol ), true, true );
        t.equal( typeofit.isSymbol( Object( symbol ) ), true, true );
        t.equal( typeofit.isSymbol( Object( robert ) ), false, false );

        t.end();
    } );

    test( 'isTextNode', function ( t ) {
        var body = HTMLDOM.window.document.querySelector( '#body' ),
            text = body.querySelector( '#p1' ).firstChild;

        t.equal( typeofit.isTextNode( text ), true, true );
        t.equal( typeofit.isTextNode( body ), false, false );

        t.end();
    } );

    test( 'isUndefined', function ( t ) {
        t.equal( typeofit.isUndefined( unset ), true, true );
        t.equal( typeofit.isUndefined( arguments ), false, false );
        t.equal( typeofit.isUndefined( [] ), false, false );
        t.equal( typeofit.isUndefined( '' ), false, false );
        t.equal( typeofit.isUndefined( 0 ), false, false );
        t.equal( typeofit.isUndefined( NaN ), false, false );
        t.equal( typeofit.isUndefined( {} ), false, false );

        t.end();
    } );

    test( 'isValue', function ( t ) {
        t.equal( typeofit.isValue( unset ), false, false );
        t.equal( typeofit.isValue( fn ), true, true );
        t.equal( typeofit.isValue( regexp ), true, true );
        t.equal( typeofit.isValue( [] ), true, true );
        t.equal( typeofit.isValue( '' ), true, true );
        t.equal( typeofit.isValue( 0 ), true, true );
        t.equal( typeofit.isValue( NaN ), false, false );
        t.equal( typeofit.isValue( {} ), true, true );
        t.equal( typeofit.isValue( null ), false, false );

        t.end();
    } );

    test( 'isWeakMap', function ( t ) {
        t.equal( typeofit.isWeakMap( weakmap ), true, true );
        t.equal( typeofit.isWeakMap( map ), false, false );

        t.end();
    } );

    test( 'isWeakSet', function ( t ) {
        t.equal( typeofit.isWeakSet( weakset ), true, true );
        t.equal( typeofit.isWeakSet( set ), false, false );

        t.end();
    } );

    test( 'isXML', function ( t ) {
        var XMLDOM = new DOMParser().parseFromString(
            '<xml xmlns="a" xmlns:c="./lite">\n' +
            '\t<child>test</child>\n' +
            '\t<child></child>\n' +
            '\t<child/>\n' +
            '</xml>', 'text/xml' );

        t.equal( typeofit.isXML( XMLDOM ), true, true );
        t.equal( typeofit.isXML( HTMLDOM ), false, false );

        t.end();
    } );
})();